package mo.qmul.gemom;


import java.awt.Button;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Label;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JSplitPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.ListSelectionModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.text.DefaultCaret;

import resilienceManager.qmul.gemom.ScenarioManager;


import com.qsphere.boundary.gemom.GEMOMBrokerAdapter;
import com.qsphere.boundary.gemom.GEMOMClientData;
import com.qsphere.boundary.gemom.GEMOMData;
import com.qsphere.boundary.gemom.GEMOMListener;
import com.qsphere.boundary.gemom.GEMOMMessageType;
import com.qsphere.boundary.gemom.GEMOMParameters;
import com.qsphere.boundary.gemom.impl.GEMOMBrokerAdapterImpl;
/** COPYRIGHT DISCLAIMER
 * Synopsis: All the source codes presented in this document are intellectual
 * properties of Queen Mary, University of London.
 * 
 * @year 2010
 * @author Peng Jiang, John Bigham, Jiayi Wu Jinfu Wang------------QMUL
 * @version 1.0 
 * */
public class ConnectionControl extends JFrame implements ActionListener, GEMOMListener{


	private static final String PUBLISH = "publish";
	private static final String SUBSCRIBE = "subscribe";
	private static final String NAMESPACE_SUBSCRIBE = "namespaceSubscribe";
	private static final String CONNECT = "connect";
	private static final String DISCONNECT = "disconnect";
	private static final String ADD = "add";
	private static final String DELETE = "delete";
	private static final String SHOW = "show";


	protected static final String VOLUMETRY_TEST_TOPIC = "__FAST1";
	protected boolean m_showVolumetryMonitorMessages = true;
	protected int m_updateCounter = 0;
	protected int m_volumetryTestPublishCount = 100;



	protected TextField ipstring= new TextField();
	protected TextField portstring= new TextField();


	protected JTextArea m_monitorTextArea = new JTextArea();
	protected DateFormat m_sdf = SimpleDateFormat
	.getTimeInstance(SimpleDateFormat.MEDIUM);

	protected JScrollPane scrollTest = new JScrollPane();
	protected JScrollPane scrollTable = new JScrollPane();
	protected JSplitPane splitPane;

	protected ConnectDialog cDialog;

	protected stateTable sTable;
	protected JScrollPane mscroll = new JScrollPane();
	protected JSplitPane splitPane2;

	protected JTable mTable;

	protected GEMOMBrokerAdapter m_brokerInterface = null;


	private Button connectButton;
	private Button disconnectButton;
	private Button addButton;
	private Button deletButton;
	private Button showButton;
	private boolean m_isConnected = false;


	protected Vector<stateRecord> measureList= new Vector<stateRecord>();


	protected Vector<TopicData> topicList = new Vector<TopicData>();


	protected int Row_selected=-1;


	private ScenarioManager up;





	public ConnectionControl(ScenarioManager own){

		up=own;

		setTitle("Connection Control");
		setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

		initialiseStartupParameters();

		this.setBounds(10, 10, 600, 600);

		//textField.setEditable(false);
		scrollTest.getViewport().add(m_monitorTextArea);

		// always update the text field view point
		DefaultCaret caret = (DefaultCaret)m_monitorTextArea.getCaret();
		caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);

		scrollTest.setPreferredSize(new Dimension(500, 100));

		sTable= new stateTable();
		scrollTable.getViewport().add(sTable.getTable());


		String[] colName={"Namespace","Topic","State"};
		DefaultTableModel model= new DefaultTableModel(colName,0);

		mTable= new JTable(model);
		mscroll.getViewport().add(mTable);


		mTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		mTable.getSelectionModel().addListSelectionListener( new ListSelectionListener(){

			@Override
			public void valueChanged(ListSelectionEvent e) {
				// TODO Auto-generated method stub


			}}

		);



		splitPane2=new JSplitPane(JSplitPane.VERTICAL_SPLIT,scrollTable,mscroll);


		splitPane= new JSplitPane(JSplitPane.VERTICAL_SPLIT,scrollTest, splitPane2);
		splitPane.setOneTouchExpandable(true);
		splitPane.setDividerLocation(150);

		getContentPane().add(splitPane, "Center");

		sTable.getTable().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);


		sTable.getTable().getSelectionModel().addListSelectionListener( new ListSelectionListener(){

			@Override
			public void valueChanged(ListSelectionEvent e) {
				// TODO Auto-generated method stub

				int row=sTable.getTable().getSelectedRow();

				if(Row_selected<0)
				{
					buildMTable(row);
					Row_selected=row;
				}
				else{
					if(row!=Row_selected)
					{
						buildMTable(row);
						Row_selected=row;
					}
				}

			}}

		);





		// set up the connection panel
		JPanel conPanel= new JPanel(new GridLayout(3, 3));

		Label l1 = new Label("IP Address", Label.CENTER);
		Label l2= new Label("Port", Label.CENTER);

		conPanel.add(l1);
		conPanel.add(ipstring);

		conPanel.add(l2);
		conPanel.add(portstring);

		connectButton = new Button("Connect");
		connectButton.setActionCommand(CONNECT);
		connectButton.addActionListener(this);
		conPanel.add(connectButton);

		disconnectButton = new Button("Disconnect");
		disconnectButton.setActionCommand(DISCONNECT);
		disconnectButton.addActionListener(this);
		conPanel.add(disconnectButton);




		getContentPane().add(conPanel,"North");


		// set up the subscriber panel
		JPanel sPanel= new JPanel(new GridLayout(1, 3));


		addButton= new Button("Add");
		addButton.setActionCommand(ADD);
		addButton.addActionListener(this);
		sPanel.add(addButton);

		deletButton= new Button("Del");
		deletButton.setActionCommand(DELETE);
		deletButton.addActionListener(this);
		sPanel.add(deletButton);

		showButton = new Button("SHOW");
		showButton.setActionCommand(SHOW);
		showButton.addActionListener(this);
		sPanel.add(showButton);


		getContentPane().add(sPanel,"South");




	}




	private void newTopicDialog(){

		int row=mTable.getSelectedRow();

		DefaultTableModel model=(DefaultTableModel) mTable.getModel();


		String ns= (String) model.getValueAt(row, 0);
		String t= (String) model.getValueAt(row, 1);
		//System.out.println("Row selection="+row);


		TopicData tpdata=null;
		for(int i=0; i< topicList.size(); i++)
		{
			tpdata=topicList.get(i);
			if(tpdata.equal(ns, t))
			{

				break;
			}

		}

		TopicDialog tdialog= new TopicDialog(this,tpdata); 

		tdialog.show();



	}




	private void buildMTable(int rowID)
	{


		
		String[] colName={"Namespace","Topic","State"};
		DefaultTableModel model= new DefaultTableModel(colName,0);

		String ns=sTable.getTable().getValueAt(rowID, 0).toString();

		//System.out.println("NS="+ns);

		for(int i=0; i< measureList.size(); i++)
		{
			stateRecord sr= measureList.get(i);

			if(ns.equalsIgnoreCase(sr.getNamespace()))
			{
				for(int j=0; j<sr.getnNumofTopic(); j++ )
				{



					Object[] rowData= new Object[3];

					rowData[0]=sr.getNamespace();


					rowData[1]=sr.getTopics().get(j);
					rowData[2]=stateOftopic(sr.getNamespace(),sr.getTopics().get(j));

					model.addRow(rowData);

				}

				mTable.setModel(model);
			}
		}

	}


	protected String stateOftopic(String ns, String t)
	{
		String value=null;


		ns=Util.addSlash(ns);


		for(int i=0; i< topicList.size(); i++)
		{
			TopicData td= topicList.get(i);

			if((ns.equalsIgnoreCase(td.getNamespace()))&&(t.equalsIgnoreCase(td.getTopic())))
			{

				if(td.getData().size()<1)
				{
					return "Not Available";

				}
				else
				{
					value=""+td.getState();
					return value;
				}

			}

		}

		return "Not Available";

	}


	protected void initialiseStartupParameters() {
		// initialize startup parameters
		// m_psProxyIP = getStartupParameter("PSProxyAddress");
		// setUserName(getStartupParameter("Username"));
		// try {
		// m_showVolumetryMonitorMessages = Boolean
		// .parseBoolean(getStartupParameter("VolumetryTestShowMonitorMessages"));
		// } catch (Exception e) {
		// }
		// try {
		// m_volumetryTestPublishCount = Integer
		// .parseInt(getStartupParameter("VolumetryTestIterationCount"));
		// } catch (Exception e) {
		// }
	}

	public void actionPerformed(ActionEvent actionEvent) {
		String command = actionEvent.getActionCommand();
		//debug("Action command: " + command + " isNamespace subscribe: "
		//		+ NAMESPACE_SUBSCRIBE.equals(command));
		if (PUBLISH.equalsIgnoreCase(command)) {
			checkIfConnected();

		} else if (SUBSCRIBE.equalsIgnoreCase(command)) {
			checkIfConnected();
		} else if (NAMESPACE_SUBSCRIBE.equalsIgnoreCase(command)) {
			checkIfConnected();
		} else if (CONNECT.equalsIgnoreCase(command)) {
			onConnect();
		} else if (DISCONNECT.equalsIgnoreCase(command)) {
			onDisconnect();
		} else if (ADD.equalsIgnoreCase(command)) {
			addMeasure();
		} else if (DELETE.equalsIgnoreCase(command)) {
			delMeasure();
		} else if (SHOW.equalsIgnoreCase(command)){
			newTopicDialog();
		}

	}



	private void addMeasure(){
		cDialog= new ConnectDialog(this);

		cDialog.setVisible(true);


	}

	/**
	 * 
	 * 
	 * **/
	public void buildMeasure(){

		stateRecord sr=cDialog.getstateRecord();

		measureList.add(sr);

		onNamespaceSubscribe(sr.getNamespace());


		sTable.createRow(sr);
	}

	/**
	 * 
	 * Delete and unSubscribe the measurement
	 * 
	 * **/
	private void delMeasure(){

		int sel=sTable.getTable().getSelectedRow();

		// un-subscriber from the name space
		String namespace =(String) sTable.getTable().getValueAt(sel, 0);

		for(int i=0; i< measureList.size(); i++)
		{
			if(namespace.equalsIgnoreCase(measureList.get(i).getNamespace()))
			{

				measureList.remove(i);

				onNamespaceUnSubscribe(namespace);

				// delete the row from the table
				sTable.deletRow(sel);

				up.respondText("Unsubscribe namespace:"+namespace);
			}
		}


	}







	private void checkIfConnected() {
		if (!m_isConnected) {
			boolean shouldConnect = JOptionPane
			.showConfirmDialog(
					this,
					"You're not connected to GEMOM. Do you want to connect first?",
					"Not Connected", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION;
			if (shouldConnect) {
				onConnect();
			}
		}
	}

	private void onConnect() {


		GEMOMParameters connectParameters = new GEMOMParameters();

		String ip= ipstring.getText();
		String port= portstring.getText();

		connectParameters.put("ConnectionString", "client@"+ip+":"+port+"\n");



		if (getGEMOMBrokerInterface().connect("TestinJNI", null,
				connectParameters, null, null)) {
			setIsConnected(true);

			up.respondText("Connect to "+ ip+":"+port);

		} else {
			JOptionPane.showMessageDialog(this, "Can't connect", "Error",
					JOptionPane.ERROR_MESSAGE);
		}
	}

	private void onDisconnect() {
		if (getGEMOMBrokerInterface().disconnect(null, null, null)) {
			setIsConnected(false);

			up.respondText("Disconnect from "+ ipstring.getText()+":"+portstring.getText()+"\n");

		} else {
			JOptionPane.showMessageDialog(this, "Can't disconnect", "Error",
					JOptionPane.ERROR_MESSAGE);
		}
	}

	private void setIsConnected(boolean isConnected) {
		m_isConnected = isConnected;
		connectButton.setEnabled(!isConnected);
		disconnectButton.setEnabled(isConnected);
		if (isConnected)
			printLineInMonitor(getTimeAsString() + ": Connected");
		else
			printLineInMonitor(getTimeAsString() + ": Disconnected");
	}



	protected void onSubscribe(String namespace,String topic)
	{

		if ((topic == null) || (topic.equalsIgnoreCase("")))
			return;

		GEMOMClientData clientData = new GEMOMClientData();
		clientData.put(topic + "_CLIENT_DATA_KEY", topic + 
				"_CLIENT_DATA_VALUE_" + (int)(Math.random() * 1000.0D));
		getGEMOMBrokerInterface().subscribeOnTopic(this, namespace, topic, 
				clientData, null);

		printLineInMonitor(getTimeAsString() + ": Subscribing on topic: " + 
				topic + " with clientData:");
		printLineInMonitor(formatHashtable(clientData));

	}



	/**
	 * Performs subscribe operation on name space entered by user
	 */

	protected void onNamespaceSubscribe(String namespace) {


		if (namespace == null || namespace.equalsIgnoreCase("")) {
			return;
		}

		up.respondText("Subscribe to "+ namespace);

		GEMOMClientData clientData = new GEMOMClientData();
		clientData.put(namespace + "_CLIENT_DATA_KEY", namespace
				+ "_CLIENT_DATA_VALUE_" + (int) (Math.random() * 1000));
		getGEMOMBrokerInterface().subscribeOnNamespace(this, namespace,
				clientData, null);

		printLineInMonitor(getTimeAsString() + ": Subscribing on namespace: "
				+ namespace + " with clientData:");
		printLineInMonitor(formatHashtable(clientData));




	}

	/**
	 * Performs subscribe operation on topic entered by user
	 */
	protected void onUnSubscribe(String namespace, String topic) {
		//String namespace = m_namespaceSubscribeTopicField.getText();
		//String topic = m_subscribeTopicField.getText();
		if (topic == null || topic.equalsIgnoreCase("")) {
			return;
		}
		GEMOMClientData clientData = new GEMOMClientData();
		clientData.put(topic + "_CLIENT_DATA_KEY", topic
				+ "_CLIENT_DATA_VALUE_" + (int) (Math.random() * 1000));
		getGEMOMBrokerInterface().unsubscribeFromTopic(this, namespace, topic,
				clientData, null);

		printLineInMonitor(getTimeAsString() + ": Unsubscribing from topic: "
				+ topic + " with clientData:");
		printLineInMonitor(formatHashtable(clientData));



	}





	/***
	 * 
	 * update the data for each topic
	 * 
	 * **/


	private void updateTopic(String namespace, String topic, String key, String value)
	{


		for(int i=0; i< topicList.size();i++)
		{
			TopicData td= topicList.get(i);


			if((namespace.equalsIgnoreCase(td.getNamespace()))&&(topic.equalsIgnoreCase(td.getTopic())))
			{

				long time_long= Long.parseLong(key);

				Date time= new Date(time_long);

				String time_s=""+time.getHours()+":"+time.getMinutes()+":"+time.getSeconds();

				Field p= new Field(time_s,value);
				//Field p= new Field(key,value);

				td.addData(p);




			}


		}


	}





	public String getGEMOMClientName() {
		return "Resilience Manager";
	}

	public boolean update(String namespace, String topic,
			GEMOMMessageType messageType, GEMOMData data,
			GEMOMClientData clientData) {





		if (shouldPrintUpdateInMonitor(topic)) {
		/*	printLineInMonitor(getTimeAsString() + ": Update on topic=" + topic
					+ " messageType: " + messageType + " with data:");
			printLineInMonitor(formatHashtable(data));
			printLineInMonitor("and client data:");
			printLineInMonitor(formatHashtable(clientData));
			*/


		}
		m_updateCounter++;

		Enumeration en = data.keys();
		String key = (String)en.nextElement();
		String value=data.get(key).toString();

		namespace = Util.addSlash(namespace);

		updateTopic(namespace, topic, key,value);
		
		int row=sTable.getTable().getSelectedRow();
		if((Row_selected==row)||(Row_selected<0))			
		{
					buildMTable(row);
					Row_selected=row;
		}


		return true;
	}



	public boolean updateNamespace(String namespace, String topic,
			String newName, GEMOMMessageType messageType,
			GEMOMClientData clientData) {



		if (messageType.equals(GEMOMMessageType.TOPIC_CREATE)) {
		//	printLineInMonitor(getTimeAsString() + ":topic " + topic + " created, subscribing to: " + namespace
		//			+ topic);
			getGEMOMBrokerInterface().subscribeOnTopic(this, namespace, topic,
					clientData, null);


			namespace = Util.addSlash(namespace);

			TopicData tn=new TopicData(namespace, topic);
			topicList.add(tn);

			up.respondText("Subscribe to "+ topic +"@"+namespace);

			for(int i=0; i< measureList.size();i++)
			{
				stateRecord sr=measureList.get(i);

				if(sr.getNamespace().equalsIgnoreCase(namespace))
					{	
						sr.addTopic(topic);
						sTable.update(sr);
					}
				

			}


		}


		return true;
	}

	protected void onNamespaceUnSubscribe(String namespace) {

		if (namespace == null || namespace.equalsIgnoreCase("")) {
			return;
		}

		GEMOMClientData clientData = new GEMOMClientData();
		clientData.put(namespace + "_CLIENT_DATA_KEY", namespace
				+ "_CLIENT_DATA_VALUE_" + (int) (Math.random() * 1000));

		// the namespace has to be embraced by slashes
		getGEMOMBrokerInterface().unsubscribeFromNamespace(this, Util.addSlash(namespace),
				clientData, null);
		/*
		printLineInMonitor(getTimeAsString() + ": Unsubscribing from namespace: "
				+ namespace + " with clientData:");
		printLineInMonitor(formatHashtable(clientData));
		*/




	}




	private boolean shouldPrintUpdateInMonitor(String topic) {
		return !VOLUMETRY_TEST_TOPIC.equals(topic)
		|| (VOLUMETRY_TEST_TOPIC.equals(topic) && m_showVolumetryMonitorMessages);
	}


	void printInMonitor(String msg) {
		m_monitorTextArea.append(msg);
	}

	void printLineInMonitor(String msg) {
		printInMonitor(msg);
		m_monitorTextArea.append("\n");
	}

	protected String getTimeAsString() {
		return m_sdf.format(new Date());
	}

	protected GEMOMBrokerAdapter getGEMOMBrokerInterface() {
		if (m_brokerInterface == null) {

			m_brokerInterface = new GEMOMBrokerAdapterImpl();

			// ((GEMOMBrokerAdapterImpl) m_brokerInterface).setApp(this);
			GEMOMParameters connectParameters = new GEMOMParameters();
			// connectParameters.put("ConnectionString",
			// "client@127.0.0.1:7891");

			// m_brokerInterface.connect("TestinJNI", null, connectParameters,
			// null, null);
		}
		return m_brokerInterface;
	}

	public static String formatHashtable(Hashtable ht) {
		return formatMap(ht, 0);
	}

	public static String formatMap(Map map) {
		return formatMap(map, 0);
	}

	public static String formatMap(Map map, int level) {
		StringBuffer sb = new StringBuffer();

		if (map != null) {
			Object[] keysArray = new Object[map.size()];

			Set keys = map.keySet();

			int i = 0;
			Iterator it = keys.iterator();
			while (it.hasNext()) {
				keysArray[i] = it.next();
				i++;
			}
			String leading = "";
			for (i = 0; i < level; i++) {
				leading += "\t";
			}
			if (keysArray != null && keysArray.length > 0
					&& keysArray[0] instanceof Comparable) {
				Arrays.sort(keysArray);
			}

			for (i = 0; i < keysArray.length; i++) {
				if (map.get(keysArray[i]) instanceof Map) {
					sb.append(leading + keysArray[i] + " =\n"
							+ formatMap((Map) map.get(keysArray[i]), level + 1)
							+ "\n");
					//if(keysArray[i].equals("aMEM"))
					//System.out.println();

				} else {
					sb.append(leading + keysArray[i] + " = "
							+ map.get(keysArray[i]) + "\n");
				}
			}
		} else {
			sb.append("null");
		}

		return sb.toString();
	}

}
